/*******************************************************************************
版权声明: Copyright(C) HT-Tech Co., Ltd. 2020-2021. All rights reserved.
文件名称: htwork.c
文件描述: SSL工作线程定义
创 建 者: 陈位仅
创建时间: 2024年11月14日
修改历史:
1. 2024年11月14日   陈位仅      创建文件 
*******************************************************************************/
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <time.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <netinet/ip.h>
#include <netinet/udp.h>
#include <netinet/tcp.h>

#include "htmacro.h"
#include "htcfg.h"
#include "htssl.h"
#include "httun.h"
#include "htwork.h"
#include "htprotocol.h"
#include "htacl.h"

void hex_dump(char *buf, int len)
{
    int i = 0;

    if (buf) {
        for (i = 0; i < len; i++) {
            printf("%02x ", buf[i] & 0xff);
            if (i % 16 == 15) {
                printf("\n");
            }
        }
        printf("\n");
    }
    fflush(stdout);

    return;
}

// 线程接收隧道数据,封装隧道报文并通过SSL隧道发送数据到网关
void htwork_tun(IN void *arg)
{
    int rv = 0;
    int rBytes = 0;
    char rBuffer[BUFFER_SIZE] = {0};    // 接收缓冲区
    htssl_info_t *pInfo = NULL;         // SSL连接信息
    char *pData = NULL;                 // 隧道数据包
    char *pSend = NULL;                 // SSL隧道数据包
    static time_t tLast = 0;            // 上次接收时间
    time_t tNow = 0;                    // 当前时间
    struct iphdr *pIp = NULL;           // IP报文头
    struct udphdr *pUdp = NULL;         // UDP报文头
    struct tcphdr *pTcp = NULL;         // TCP报文头
    short dport = 0;

    // 获取SSL连接信息
    pInfo = arg;

    // 从虚拟网卡接收数据
    pData = rBuffer + HT_OFFSET;
    rBytes = httun_recv(pInfo, pData, BUFFER_SIZE - HT_OFFSET);
    if (rBytes <= 0) {
        return;
    }

    // ACL过滤
    if (pInfo->pcfg->active) {
        pIp = (void *)pData;
        switch (pIp->protocol)
        {
        case IPPROTO_UDP:
            pUdp = (void *)(pData + (pIp->ihl & 0x0f) * 4);
            dport = ntohs(pUdp->dest);
            break;
        
        case IPPROTO_TCP:
            pTcp = (void *)(pData + (pIp->ihl & 0x0f) * 4);
            dport = ntohs(pTcp->dest);
            break;

        default:
            return;
        }
        rv = htacl_check(pIp->daddr, dport);
        if (rv) {
            return;
        }
    }

    // 封装隧道数据包
    pSend = htprc_data(pData, &rBytes, _HT_TYPE_TUNNEL_DATA);
    if (NULL == pSend) {
        return;
    }

    // 发送数据
    if (SSL_write(pInfo->ssl, pSend, rBytes) <= 0) {
        ERR_print_errors_fp(stderr);
        exit(1);     // SSL隧道断开,先退出,后续再设计重连
    }

    // 获取系统当前时间
    if (pInfo->pcfg->heartbeat) {
        tNow = time(NULL);
        if (tNow - tLast > pInfo->pcfg->heartbeat) {
            rv = htprc_heartbeat(rBuffer, &rBytes); // 心跳包构造
            if (rv < 0) {
                return;
            }

            if (SSL_write(pInfo->ssl, rBuffer, rBytes) <= 0) {
                ERR_print_errors_fp(stderr);
                exit(1);     // SSL隧道断开,先退出,后续再设计重连
            }
            tLast = tNow;
        }
    }

    return;
}

// 线程接收SSL隧道数据,解析隧道报文并通过发送到虚拟网卡
void htwork_ssl(IN void *arg)
{
    int rBytes = 0;
    char rBuffer[BUFFER_SIZE] = {0};    // 接收缓冲区
    htssl_info_t *pInfo = NULL;         // SSL连接信息
    ht_head_t *pHead = NULL;            // SSL隧道数据包
    char *pData = NULL;                 // 隧道数据包

    pInfo = arg;
    // 接收数据
    rBytes = SSL_read(pInfo->ssl, rBuffer, BUFFER_SIZE);
    if (rBytes <= 0) {
        ERR_print_errors_fp(stderr);
        exit(1);     // SSL隧道断开,先退出,后续再设计重连
    } else {
        // 解析数据包
        pHead = (void *)rBuffer;
        if (_HT_VERSION != ntohs(pHead->ht_version)) {
            return;
        }

        // 隧道数据包
        if (ntohs(_HT_TYPE_TUNNEL_DATA) == pHead->ht_type) {
            // 数据包定位
            pData = pHead->ht_data;
            httun_send(pInfo, pData, rBytes - sizeof(ht_head_t));
        }
    }

    return;
}

// 线程接收本地代理数据,封装本地代理数据并通过SSL隧道发送数据到网关
void htwork_local(IN void *arg)
{
    int rv = 0;
    int rBytes = 0;
    char rBuffer[BUFFER_SIZE] = {0};    // 接收缓冲区
    htssl_info_t *pInfo = NULL;         // SSL连接信息
    static time_t tLast = 0;            // 上次接收时间
    time_t tNow = 0;                    // 当前时间

    // 获取SSL连接信息
    pInfo = arg;
    // 接收本地代理数据
    if (pInfo->fdLocal) {
        rBytes = recv(pInfo->fdLocal, rBuffer, BUFFER_SIZE, 0);
        if (rBytes > 0) {
            // 发送数据
            if (SSL_write(pInfo->ssl, rBuffer, rBytes) <= 0) {
                ERR_print_errors_fp(stderr);
                exit(1);     // SSL隧道断开,先退出,后续再设计重连
            }
        }
    } else {
        // 监听本地代理连接
        pInfo->fdLocal = accept(pInfo->fdTun, NULL, NULL);
        if (pInfo->fdLocal < 0) {
            pInfo->fdLocal = 0;
        }
    }

    // 获取系统当前时间
    if (pInfo->pcfg->heartbeat) {
        tNow = time(NULL);
        if (tNow - tLast > pInfo->pcfg->heartbeat) {
            rv = htprc_heartbeat(rBuffer, &rBytes); // 心跳包构造
            if (rv < 0) {
                return;
            }

            if (SSL_write(pInfo->ssl, rBuffer, rBytes) <= 0) {
                ERR_print_errors_fp(stderr);
                exit(1);     // SSL隧道断开,先退出,后续再设计重连
            }
            tLast = tNow;
        }
    }

    return;
}

// 线程接收SSL隧道数据,解析隧道报文并通过发送到本地代理
void htwork_server(IN void *arg)
{
    int rBytes = 0;
    char rBuffer[BUFFER_SIZE] = {0};    // 接收缓冲区
    htssl_info_t *pInfo = NULL;         // SSL连接信息
    ht_head_t *pHead = NULL;            // SSL隧道数据包
    char *pData = NULL;                 // 隧道数据包

    pInfo = arg;
    // 接收数据
    rBytes = SSL_read(pInfo->ssl, rBuffer, BUFFER_SIZE);
    if (rBytes <= 0) {
        ERR_print_errors_fp(stderr);
        exit(1);     // SSL隧道断开,先退出,后续再设计重连
    } else {
        // 数据发送到本地代理
        if (pInfo->fdLocal) {
            send(pInfo->fdLocal, rBuffer, rBytes, 0);
        }
    }

    return;
}

// SSL隧道心跳包处理线程
void htwork_heart(IN void *arg)
{
    int rv = 0;
    int sBytes = 0;
    char sBuffer[BUFFER_SIZE] = {0};    // 发送缓冲区
    htssl_info_t *pInfo = NULL;         // SSL连接信息
 
    pInfo = arg;
    rv = htprc_heartbeat(sBuffer, &sBytes);
    if (rv < 0) {
        return;
    }

    // 发送数据
    if (SSL_write(pInfo->ssl, sBuffer, sBytes) <= 0) {
        ERR_print_errors_fp(stderr);
        exit(1);     // SSL隧道断开,先退出,后续再设计重连
    }
    sleep(3);

    return;
}
